import {EventEmitter} from 'events';
import {nanoid} from 'nanoid';
import {PlotTypes} from '../enum/PlotTypes';
import * as Plots from '../geometry';
import * as PlotUtils from '../utils';
import {Point} from '../type';
import {formatPlotData, rgb2Rgba} from '../utils/FormatUtil';

class PlotDraw extends EventEmitter {
    public plotType: PlotTypes | null = null;
    public plotParams: any;

    private points: Array<Point> = [];
    private plot: any;
    private currentFeature: any;
    private currentPlotParams: any;
    private currentMapViewport: any;
    private dbClickZoomInteraction: any;
    private drawInteraction: any;

    private readonly plotLayer: any;
    private readonly _map: any;
    private readonly _options: any;


    constructor(map: any, params?: any) {
        super();
        this._map = map;
        this._options = params || {};
        this.currentMapViewport = map.getViewport();
        this.plotLayer = map.getLayers().getArray().find((item: any): any => item.get('name') === 'plot-layer');
    }

    /**
     * 激活工具
     * @param type
     * @param params
     */
    public activate(type: PlotTypes, params: any = {}): void {
        this.deactivate();
        this.deactivateMapTools();
        this.plotType = type;
        this.plotParams = params;
        if (type === PlotTypes.TEXT_AREA) {
            this.activeInteraction();
        } else if (Object.values(PlotTypes).includes(type)) {
            this._map.on('click', this.mapFirstClickHandler);
        } else {
            console.warn('不存在的标绘类型');
        }
    }

    public deactivate(): void {
        this.removeEventHandlers();
        if (this.drawInteraction) {
            this._map.removeInteraction(this.drawInteraction);
            this.drawInteraction = null;
        }
        this.points = [];
        this.plot = null;
        this.currentFeature = null;
        this.plotType = null;
        this.plotParams = null;
        this.activeMapTools();
    }

    public isDrawing(): boolean {
        return !!this.plotType;
    }

    /**
     * 激活文本交互工具
     */
    private activeInteraction = (): void => {
        // @ts-ignore
        this.drawInteraction = new ol.interaction.Draw({
            // @ts-ignore
            style: new ol.style.Style({
                // @ts-ignore
                fill: new ol.style.Fill({
                    color: 'rgba(255, 255, 255, 0.7)'
                }),
                // @ts-ignore
                stroke: new ol.style.Stroke({
                    color: 'rgba(0, 0, 0, 0.15)',
                    width: 2
                }),
                // @ts-ignore
                image: new ol.style.Icon({
                    anchor: [1, 1],
                    anchorXUnits: 'fraction',
                    anchorYUnits: 'fraction',
                    opacity: 0.75,
                    src: ''
                })
            }),
            type: 'Circle',
            // @ts-ignore
            geometryFunction: ol.interaction.Draw.createBox()
        });
        this._map.addInteraction(this.drawInteraction);
        this.drawInteraction.on('drawend', this.textAreaDrawEnd);
    };

    /**
     * 文本标绘绘制完成
     * @param event
     */
    private textAreaDrawEnd = (event: any): void => {
        if (event && event.feature) {
            this._map.removeInteraction(this.drawInteraction);
            const extent: Array<number> = event.feature.getGeometry().getExtent();
            const center: Array<number> = [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2];
            const topLeft: Array<number> = this._map.getPixelFromCoordinate([extent[0], extent[1]]);
            const bottomRight: Array<number> = this._map.getPixelFromCoordinate([extent[2], extent[3]]);
            const [width, height]: [width: number, height: number] = [Math.abs(topLeft[0] - bottomRight[0]), Math.abs(topLeft[1] - bottomRight[1])];
            if (this._map) {
                // console.log(formatPlotData(this.plotType as PlotTypes, plotText));
                // this._map.addOverlay(plotText);
                this.emit('drawEnd', {
                    type: 'drawEnd',
                    originalEvent: event,
                    plotType: 'text',
                    data: {
                        id: nanoid(),
                        position: center,
                        value: '',
                        width,
                        height,
                        style: {
                            width: `${width}px`,
                            height: `${height}px`
                        }
                    }
                });
            } else {
                console.warn('未传入地图对象或者plotText创建失败！');
            }
        } else {
            console.info('未获取到要素！');
        }
    };

    /**
     * 鼠标首次点击地图
     * @param event
     */
    private mapFirstClickHandler = (event: MouseEvent): void => {
        this._map.un('click', this.mapFirstClickHandler);
        this.points.push((event as any).coordinate);
        this.plot = this.createPlot(this.plotType as PlotTypes, this.points, this.plotParams);
        // @ts-ignore
        this.currentFeature = new ol.Feature(this.plot);
        this.currentFeature.set('isPlot', true);
        const style: any = this.createStyle(this.plotType as PlotTypes, this.plotParams);
        this.currentFeature.setStyle(style);

        this.plotLayer.getSource().addFeature(this.currentFeature);
        if (this.plotType === PlotTypes.POINT) {
            this.plotParams['plotType'] = this.plotType;
            this.currentFeature.setProperties(this.plotParams, undefined);
            this.plot.finishDrawing();
            this.drawEnd(event);
        } else {
            this._map.on('click', this.mapNextClickHandler);
            if (!this.plot.freehand) {
                this._map.on('dblclick', this.mapDoubleClickHandler);
            }
            this._map.un('pointermove', this.mapMouseMoveHandler);
            this._map.on('pointermove', this.mapMouseMoveHandler);
        }
        if (this.plotType && this.currentFeature) {
            this.plotParams['plotType'] = this.plotType;
            this.currentFeature.setProperties(this.plotParams, undefined);
        }
    };

    private mapNextClickHandler = (event: MouseEvent): void => {
        const coordinate: Point = (event as any).coordinate;
        if (!this.plot.freehand) {
            if (PlotUtils.mathDistance(coordinate, this.points[this.points.length - 1]) < 0.0001) return;
        }
        this.points.push(coordinate);
        this.plot.setPoints(this.points);
        if (this.plot.fixPointCount === this.plot.getPointCount()) {
            this.mapDoubleClickHandler(event);
        }
        if (this.plot && this.plot.freehand) {
            this.mapDoubleClickHandler(event);
        }
    };

    private mapDoubleClickHandler = (event: MouseEvent): void => {
        event.preventDefault();
        this.plot.finishDrawing();
        this.drawEnd(event);
    };

    private mapMouseMoveHandler = (event: MouseEvent): void => {
        const coordinate: Point = (event as any).coordinate;
        if (PlotUtils.mathDistance(coordinate, this.points[this.points.length - 1]) < 0.0001) return;
        if (!this.plot.freehand) {
            const newPoints: Array<Array<number>> = this.points.concat([coordinate]);
            this.plot.setPoints(newPoints);
        } else {
            this.points.push(coordinate);
            this.plot.setPoints(this.points);
        }
    };

    /**
     * 创建标绘
     * @param type
     * @param points
     * @param params
     */
    public createPlot = (type: PlotTypes, points: Array<Point>, params: any): any => {
        switch (type) {
            case PlotTypes.TEXT_AREA:
                return 'TextArea';
            case PlotTypes.SEGMENT_LINE:
                return new Plots.SegmentLine([], points, params || {});
            case PlotTypes.FREE_HAND_LINE:
                return new Plots.FreeLine([], points, params || {});
            case PlotTypes.POLYLINE:
                return new Plots.Polyline([], points, params || {});
            case PlotTypes.CIRCLE:
                return new Plots.Circle([], points, params || {});
            case PlotTypes.RECTANGLE:
                return new Plots.Rectangle([], points, params || {});
            case PlotTypes.POLYGON:
                return new Plots.Polygon([], points, params || {});
            case PlotTypes.RECT_FLAG:
                return new Plots.RectFlag([], points, params || {});
            case PlotTypes.TRIANGLE_FLAG:
                return new Plots.TriangleFlag([], points, params || {});
            case PlotTypes.DOUBLE_ARROW:
                return new Plots.DoubleArrow([], points, params || {});
            case PlotTypes.SQUAD_COMBAT:
                return new Plots.SquadCombat([], points, params || {});
            case PlotTypes.FINE_ARROW:
                return new Plots.FineArrow([], points, params || {});
            case PlotTypes.STRAIGHT_ARROW:
                return new Plots.StraightArrow([], points, params || {});
            case PlotTypes.POINT:
                return new Plots.Marker([], points, params || {});
        }
    };

    public createStyle = (type: PlotTypes, params: any): any => {
        switch (type) {
            case PlotTypes.POINT:
                const [width, height]: [width: number, height: number] = params.size;
                // @ts-ignore
                return new ol.style.Style({
                    // @ts-ignore
                    image: new ol.style.Icon({
                        anchor: [width * 0.5, height * 0.5],
                        anchorXUnits: 'pixels',
                        anchorYUnits: 'pixels',
                        src: params.url
                    })
                });
            case PlotTypes.CIRCLE:
                // @ts-ignore
                return new ol.style.Style({
                    // @ts-ignore
                    stroke: new ol.style.Stroke({
                        color: rgb2Rgba(params.strokeColor, params.strokeOpacity),
                        width: params.strokeWeight
                    }),
                    // @ts-ignore
                    fill: new ol.style.Fill({
                        color: rgb2Rgba(params.fillColor, params.fillOpacity)
                    })
                });
            case PlotTypes.FREE_HAND_LINE:
            case PlotTypes.SEGMENT_LINE:
            case PlotTypes.POLYLINE:
                // @ts-ignore
                return new ol.style.Style({
                    // @ts-ignore
                    stroke: new ol.style.Stroke({
                        color: rgb2Rgba(params.color, params.opacity),
                        width: params.weight
                    })
                });
            case PlotTypes.RECTANGLE:
            case PlotTypes.POLYGON:
            case PlotTypes.RECT_FLAG:
            case PlotTypes.TRIANGLE_FLAG:
            case PlotTypes.DOUBLE_ARROW:
            case PlotTypes.FINE_ARROW:
            case PlotTypes.STRAIGHT_ARROW:
            case PlotTypes.SQUAD_COMBAT:
                // @ts-ignore
                return new ol.style.Style({
                    // @ts-ignore
                    stroke: new ol.style.Stroke({
                        color: rgb2Rgba(params.strokeColor, params.strokeOpacity),
                        width: params.strokeWeight
                    }),
                    // @ts-ignore
                    fill: new ol.style.Fill({
                        color: rgb2Rgba(params.fillColor, params.fillOpacity)
                    })
                });
        }
    };

    private removeEventHandlers = (): void => {
        this._map.un('click', this.mapFirstClickHandler);
        this._map.un('click', this.mapNextClickHandler);
        this._map.un('pointermove', this.mapMouseMoveHandler);
        this._map.un('dblclick', this.mapDoubleClickHandler);
    };

    /**
     * 绘制完成
     * @param event
     */
    private drawEnd = (event: MouseEvent): void => {
        this.emit('drawEnd', {
            type: 'drawEnd',
            originalEvent: event,
            feature: this.currentFeature,
            data: formatPlotData(this.plotType as PlotTypes, this.currentFeature)
        });
        if (this.currentFeature && this._options['isClear']) {
            this.plotLayer.getSource().removeFeature(this.currentFeature);
        }
        this.deactivate();
    };

    private addFeature = (): void => {
        // @ts-ignore
        this.currentFeature = new ol.Feature(this.plot);
        if (this.currentFeature && this.plotLayer) {
            this.plotLayer.getSource().addFeature(this.currentFeature);
        }
    };
    /**
     * 取消激活地图交互工具（地图双击）
     */
    private deactivateMapTools = (): void => {
        const interactions: Array<any> = this._map.getInteractions().getArray();
        interactions.every((item: any): boolean => {
            // @ts-ignore
            if (item instanceof ol.interaction.DoubleClickZoom) {
                this.dbClickZoomInteraction = item;
                this._map.removeInteraction(item);
                return false;
            } else {
                return true;
            }
        });
    };
    /**
     * 激活已取消的地图工具
     */
    private activeMapTools = (): void => {
        // @ts-ignore
        if (this.dbClickZoomInteraction && this.dbClickZoomInteraction instanceof ol.interaction.DoubleClickZoom) {
            this._map.addInteraction(this.dbClickZoomInteraction);
            this.dbClickZoomInteraction = null;
        }
    };
}

export default PlotDraw;
